// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <wallet/wallet.h>

#include <qt/forms/ui_receivecoinsdialog.h>
#include <qt/receivecoinsdialog.h>

#include <qt/addresstablemodel.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/receiverequestdialog.h>
#include <qt/recentrequeststablemodel.h>
#include <qt/walletmodel.h>

#include <QAction>
#include <QCursor>
#include <QMessageBox>
#include <QScrollBar>
#include <QTextDocument>

ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *_platformStyle,
                                       QWidget *parent)
    : QDialog(parent), ui(new Ui::ReceiveCoinsDialog),
      columnResizingFixer(nullptr), model(nullptr),
      platformStyle(_platformStyle) {
    ui->setupUi(this);

    if (!_platformStyle->getImagesOnButtons()) {
        ui->clearButton->setIcon(QIcon());
        ui->receiveButton->setIcon(QIcon());
        ui->showRequestButton->setIcon(QIcon());
        ui->removeRequestButton->setIcon(QIcon());
    } else {
        ui->clearButton->setIcon(
            _platformStyle->SingleColorIcon(":/icons/remove"));
        ui->receiveButton->setIcon(
            _platformStyle->SingleColorIcon(":/icons/receiving_addresses"));
        ui->showRequestButton->setIcon(
            _platformStyle->SingleColorIcon(":/icons/edit"));
        ui->removeRequestButton->setIcon(
            _platformStyle->SingleColorIcon(":/icons/remove"));
    }

    // context menu actions
    QAction *copyURIAction = new QAction(tr("Copy URI"), this);
    QAction *copyLabelAction = new QAction(tr("Copy label"), this);
    QAction *copyMessageAction = new QAction(tr("Copy message"), this);
    QAction *copyAmountAction = new QAction(tr("Copy amount"), this);

    // context menu
    contextMenu = new QMenu(this);
    contextMenu->addAction(copyURIAction);
    contextMenu->addAction(copyLabelAction);
    contextMenu->addAction(copyMessageAction);
    contextMenu->addAction(copyAmountAction);

    // context menu signals
    connect(ui->recentRequestsView, &QWidget::customContextMenuRequested, this,
            &ReceiveCoinsDialog::showMenu);
    connect(copyURIAction, &QAction::triggered, this,
            &ReceiveCoinsDialog::copyURI);
    connect(copyLabelAction, &QAction::triggered, this,
            &ReceiveCoinsDialog::copyLabel);
    connect(copyMessageAction, &QAction::triggered, this,
            &ReceiveCoinsDialog::copyMessage);
    connect(copyAmountAction, &QAction::triggered, this,
            &ReceiveCoinsDialog::copyAmount);

    connect(ui->clearButton, &QPushButton::clicked, this,
            &ReceiveCoinsDialog::clear);
}

void ReceiveCoinsDialog::setModel(WalletModel *_model) {
    this->model = _model;

    if (_model && _model->getOptionsModel()) {
        _model->getRecentRequestsTableModel()->sort(
            RecentRequestsTableModel::Date, Qt::DescendingOrder);
        connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged,
                this, &ReceiveCoinsDialog::updateDisplayUnit);
        updateDisplayUnit();

        QTableView *tableView = ui->recentRequestsView;

        tableView->verticalHeader()->hide();
        tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        tableView->setModel(_model->getRecentRequestsTableModel());
        tableView->setAlternatingRowColors(true);
        tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
        tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);
        tableView->setColumnWidth(RecentRequestsTableModel::Date,
                                  DATE_COLUMN_WIDTH);
        tableView->setColumnWidth(RecentRequestsTableModel::Label,
                                  LABEL_COLUMN_WIDTH);
        tableView->setColumnWidth(RecentRequestsTableModel::Amount,
                                  AMOUNT_MINIMUM_COLUMN_WIDTH);

        connect(tableView->selectionModel(),
                &QItemSelectionModel::selectionChanged, this,
                &ReceiveCoinsDialog::recentRequestsView_selectionChanged);
        // Last 2 columns are set by the columnResizingFixer, when the table
        // geometry is ready.
        columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(
            tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this);

        // Set the button to be enabled or disabled based on whether the wallet
        // can give out new addresses.
        ui->receiveButton->setEnabled(model->wallet().canGetAddresses());

        // Enable/disable the receive button if the wallet is now able/unable to
        // give out new addresses.
        connect(model, &WalletModel::canGetAddressesChanged, [this] {
            ui->receiveButton->setEnabled(model->wallet().canGetAddresses());
        });
    }
}

ReceiveCoinsDialog::~ReceiveCoinsDialog() {
    delete ui;
}

void ReceiveCoinsDialog::clear() {
    ui->reqAmount->clear();
    ui->reqLabel->setText("");
    ui->reqMessage->setText("");
    updateDisplayUnit();
}

void ReceiveCoinsDialog::reject() {
    clear();
}

void ReceiveCoinsDialog::accept() {
    clear();
}

void ReceiveCoinsDialog::updateDisplayUnit() {
    if (model && model->getOptionsModel()) {
        ui->reqAmount->setDisplayUnit(
            model->getOptionsModel()->getDisplayUnit());
    }
}

void ReceiveCoinsDialog::on_receiveButton_clicked() {
    if (!model || !model->getOptionsModel() || !model->getAddressTableModel() ||
        !model->getRecentRequestsTableModel()) {
        return;
    }

    QString address;
    QString label = ui->reqLabel->text();
    /* Generate new receiving address */
    OutputType address_type = model->wallet().getDefaultAddressType();
    address = model->getAddressTableModel()->addRow(AddressTableModel::Receive,
                                                    label, "", address_type);

    switch (model->getAddressTableModel()->getEditStatus()) {
        case AddressTableModel::EditStatus::OK: {
            // Success
            SendCoinsRecipient info(address, label, ui->reqAmount->value(),
                                    ui->reqMessage->text());
            ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
            dialog->setAttribute(Qt::WA_DeleteOnClose);
            dialog->setModel(model);
            dialog->setInfo(info);
            dialog->show();

            /* Store request for later reference */
            model->getRecentRequestsTableModel()->addNewRequest(info);
            break;
        }
        case AddressTableModel::EditStatus::WALLET_UNLOCK_FAILURE:
            QMessageBox::critical(this, windowTitle(),
                                  tr("Could not unlock wallet."),
                                  QMessageBox::Ok, QMessageBox::Ok);
            break;
        case AddressTableModel::EditStatus::KEY_GENERATION_FAILURE:
            QMessageBox::critical(this, windowTitle(),
                                  tr("Could not generate new %1 address")
                                      .arg(QString::fromStdString(
                                          FormatOutputType(address_type))),
                                  QMessageBox::Ok, QMessageBox::Ok);
            break;
        // These aren't valid return values for our action
        case AddressTableModel::EditStatus::INVALID_ADDRESS:
        case AddressTableModel::EditStatus::DUPLICATE_ADDRESS:
        case AddressTableModel::EditStatus::NO_CHANGES:
            assert(false);
    }
    clear();
}

void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(
    const QModelIndex &index) {
    const RecentRequestsTableModel *submodel =
        model->getRecentRequestsTableModel();
    ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
    dialog->setModel(model);
    dialog->setInfo(submodel->entry(index.row()).recipient);
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->show();
}

void ReceiveCoinsDialog::recentRequestsView_selectionChanged(
    const QItemSelection &selected, const QItemSelection &deselected) {
    // Enable Show/Remove buttons only if anything is selected.
    bool enable =
        !ui->recentRequestsView->selectionModel()->selectedRows().isEmpty();
    ui->showRequestButton->setEnabled(enable);
    ui->removeRequestButton->setEnabled(enable);
}

void ReceiveCoinsDialog::on_showRequestButton_clicked() {
    if (!model || !model->getRecentRequestsTableModel() ||
        !ui->recentRequestsView->selectionModel()) {
        return;
    }
    QModelIndexList selection =
        ui->recentRequestsView->selectionModel()->selectedRows();

    for (const QModelIndex &index : selection) {
        on_recentRequestsView_doubleClicked(index);
    }
}

void ReceiveCoinsDialog::on_removeRequestButton_clicked() {
    if (!model || !model->getRecentRequestsTableModel() ||
        !ui->recentRequestsView->selectionModel()) {
        return;
    }
    QModelIndexList selection =
        ui->recentRequestsView->selectionModel()->selectedRows();
    if (selection.empty()) {
        return;
    }
    // correct for selection mode ContiguousSelection
    QModelIndex firstIndex = selection.at(0);
    model->getRecentRequestsTableModel()->removeRows(
        firstIndex.row(), selection.length(), firstIndex.parent());
}

// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void ReceiveCoinsDialog::resizeEvent(QResizeEvent *event) {
    QWidget::resizeEvent(event);
    columnResizingFixer->stretchColumnWidth(RecentRequestsTableModel::Message);
}

QModelIndex ReceiveCoinsDialog::selectedRow() {
    if (!model || !model->getRecentRequestsTableModel() ||
        !ui->recentRequestsView->selectionModel()) {
        return QModelIndex();
    }
    QModelIndexList selection =
        ui->recentRequestsView->selectionModel()->selectedRows();
    if (selection.empty()) {
        return QModelIndex();
    }
    // correct for selection mode ContiguousSelection
    QModelIndex firstIndex = selection.at(0);
    return firstIndex;
}

// copy column of selected row to clipboard
void ReceiveCoinsDialog::copyColumnToClipboard(int column) {
    QModelIndex firstIndex = selectedRow();
    if (!firstIndex.isValid()) {
        return;
    }
    GUIUtil::setClipboard(model->getRecentRequestsTableModel()
                              ->index(firstIndex.row(), column)
                              .data(Qt::EditRole)
                              .toString());
}

// context menu
void ReceiveCoinsDialog::showMenu(const QPoint &point) {
    if (!selectedRow().isValid()) {
        return;
    }
    contextMenu->exec(QCursor::pos());
}

// context menu action: copy URI
void ReceiveCoinsDialog::copyURI() {
    QModelIndex sel = selectedRow();
    if (!sel.isValid()) {
        return;
    }

    const RecentRequestsTableModel *const submodel =
        model->getRecentRequestsTableModel();
    const QString uri =
        GUIUtil::formatBitcoinURI(submodel->entry(sel.row()).recipient);
    GUIUtil::setClipboard(uri);
}

// context menu action: copy label
void ReceiveCoinsDialog::copyLabel() {
    copyColumnToClipboard(RecentRequestsTableModel::Label);
}

// context menu action: copy message
void ReceiveCoinsDialog::copyMessage() {
    copyColumnToClipboard(RecentRequestsTableModel::Message);
}

// context menu action: copy amount
void ReceiveCoinsDialog::copyAmount() {
    copyColumnToClipboard(RecentRequestsTableModel::Amount);
}
